package com.example.logfile;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

@Slf4j
public class LogFileApplication {
    private static final int LOG_SIZE_MB=500;
    private static final String LOG_FILE_PATH="large.log";
    private static final int LINES_PER_PAGE=1000;
    private static final String[] LOG_LEVELS={"INFO","WARN","ERROR","DEBUG"};
    private static final String[] LOG_MESSAGES = {
            "User login success, userId=",
            "Failed to connect to database",
            "Transaction completed",
            "System startup",
            "Cache cleared"
    };
    

    public static void main(String[] args) {
        try {
            if(!Files.exists(Paths.get(LOG_FILE_PATH))||
            Files.size(Paths.get(LOG_FILE_PATH))<LOG_SIZE_MB*1024*1024){
                buildLargeLogFile();
            }
            methodA();
            methodB();
            
        } catch (Exception e) {
            log.error("系统异常，e",e);
        }
    }
    
    private static void buildLargeLogFile() throws IOException {
        log.info("正在构建大型日志文件");
        long startTime = System.currentTimeMillis();
        long targetSize=LOG_SIZE_MB*1024*1024;
        Random random = new Random();
        try(BufferedWriter writer=new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(LOG_FILE_PATH), StandardCharsets.UTF_8))){
            long currentSize=0;
            int userId=1;
            while (currentSize<targetSize){
                String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                String level = LOG_LEVELS[random.nextInt(LOG_LEVELS.length)];
                String message;
                if (level.equals("INFO") && random.nextBoolean()) {
                    message = LOG_MESSAGES[0] + userId++;
                } else {
                    message = LOG_MESSAGES[random.nextInt(LOG_MESSAGES.length)];
                }
                String line = timestamp + " " + level + " " + message + "\n";
                writer.write(line);
                currentSize += line.getBytes(StandardCharsets.UTF_8).length;
            }
            long endTime = System.currentTimeMillis();
            log.info("日志文件创建完成，大小：{}MB，耗时：{}ms", targetSize/(1024*1024), endTime-startTime);
        }
    }
    
    private static void methodA(){
        log.info("正在一次性读取日志文件");
        long startTime = System.currentTimeMillis();    
        //峰值内存使用
        long peekMemory=0;
        Exception exception=null;
        List<String> lines = null;
        try {
            //记录初始内存的使用情况
            Runtime runtime = Runtime.getRuntime();
            long usedMemoryBefore=runtime.totalMemory()-runtime.freeMemory();
            //一次性读取数据
            lines=Files.readAllLines(Paths.get(LOG_FILE_PATH),StandardCharsets.UTF_8);
            log.info("日志文件共有{}行,正在输出前10行",lines.size());
            for(int i=0;i<Math.min(10,lines.size());i++){
                log.info(lines.get( i));
            }
            log.info("一次性读取日志文件输出完毕");
            long usedMemoryAfter=runtime.totalMemory()-runtime.freeMemory();
            peekMemory=(usedMemoryAfter-usedMemoryBefore)/1024/1024;
        } catch (Exception e) {
            exception=e;
        } finally {
            long endTime = System.currentTimeMillis();
            log.info("[Method A] Total time:{}",endTime-startTime);
            log.info("[Method A] Peak memory:{}MB",peekMemory);
            log.info("[Method A] Exception:{}",exception!=null?exception.toString():"NONE");
        }

    }
    
    //分页流式读取
    private static void methodB(){ 
        log.info("正在分页流式读取日志文件");
        log.info("命令：空格键（下一页）|b（上一页）|q（退出）");
        long startTime = System.currentTimeMillis();
        long peekMemory=0;
        Exception exception=null;
        //记录当前页
        int currentPage=1;
        int linesRead=0;
        try(BufferedReader reader=new BufferedReader(new InputStreamReader(new FileInputStream(LOG_FILE_PATH),StandardCharsets.UTF_8))){
            Runtime runtime = Runtime.getRuntime();
            long usedMemoryBefore=runtime.totalMemory()-runtime.freeMemory();
            //计算总行数，用于分页
            long totalLines = countTotalLines();
            int totalPages=(int)Math.ceil((double)totalLines/LINES_PER_PAGE);
            Scanner scanner = new Scanner(System.in);
            while(true){
                // 重置读取位置
                reader.mark((int) totalLines);
                //跳转到当前页的起始行
                for(int i=0;i<(currentPage-1)*LINES_PER_PAGE&&reader.readLine()!=null;i++);
                log.info("=======第{}页，共{}页========",currentPage,totalPages);
                //当前页的行
                int pageLines=0;
                String line;
                while(pageLines<LINES_PER_PAGE&&(line=reader.readLine())!=null){
                    log.info(line);
                    pageLines++;
                    linesRead++;
                }

                // 内存监控,每次读取完都取最大值
                if (linesRead % 10000 == 0) {
                    long currentMemory = (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024);
                    if (currentMemory > peekMemory) {
                        peekMemory = currentMemory;
                    }
                }
                log.info("命令：空格键（下一页）|b（上一页）|q（退出）");
                //处理用户输入
                String command=scanner.nextLine().trim();
                if (command.isEmpty() || command.equals(" ")) {
                    // 下一页
                    if (currentPage < totalPages) {
                        currentPage++;
                    } else {
                        log.info("已经是最后一页");
                    }
                } else if (command.equalsIgnoreCase("b")) {
                    // 上一页
                    if (currentPage > 1) {
                        currentPage--;
                    } else {
                        log.info("已经是第一页");
                    }
                } else if (command.equalsIgnoreCase("q")) {
                    // 退出
                    break;
                } else {
                    log.info("未知命令: " + command);
                }

                // 重置reader到标记位置
                reader.reset();
                
            }
            // 计算最终峰值内存使用
            long usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
            peekMemory = Math.max(peekMemory, (usedMemoryAfter - usedMemoryBefore) / (1024 * 1024));

        }catch (Exception e){
            exception=e;
        }finally {
            long endTime = System.currentTimeMillis();
            log.info("[Method B] Total time:{}",endTime-startTime);
            log.info("[Method B] Peak memory:{}MB",peekMemory);
            log.info("[Method B] Lines read:{}",linesRead);
            log.info("[Method B] Exception:{}",exception!=null?exception.toString():"NONE");
        }
    }
    
    //计算文件总行数，用于分页
    private static long countTotalLines() throws IOException {
        try(BufferedReader reader=new BufferedReader(new InputStreamReader(new FileInputStream(LOG_FILE_PATH),StandardCharsets.UTF_8))){
            long count=0;
            String line;
            while((line=reader.readLine())!=null)count++;
            return count;
        }
    }
    
    

}
